Knowledge: Introduce the wp_knowledge custom post type#12201
Conversation
|
Hi there! 👋 Thank you for your contribution to WordPress! 💖 It looks like this is your first pull request to No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description. Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making. More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook. Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook. If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook. The Developer Hub also documents the various coding standards that are followed:
Thank you, |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
5530424 to
41b499b
Compare
wp_knowledge custom post type
2f83ce3 to
69b1806
Compare
Register wp_knowledge, a private-by-default storage primitive for structured site knowledge, together with the wp_knowledge_type taxonomy. Both are built-in and headless (no admin UI); rows are managed through the REST API. Add WP_REST_Knowledge_Controller at /wp/v2/knowledge: reads require an authenticated user, collections are scoped to rows the current user can read, callers without the publish capability are limited to the private status, and new rows default to private. Revisions use the default controller; autosave endpoints are disabled. Synthesize a graded capability set through the user_has_cap filter: administrators manage all knowledge, while contributors and above create and manage their own private rows. Knowledge types are filterable via wp_knowledge_types(), with note as the save-time fallback. Trac ticket: https://core.trac.wordpress.org/ticket/65476 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69b1806 to
d008aaa
Compare
Aligns the experimental Knowledge implementation with the refined version ported to WordPress core in WordPress/wordpress-develop#12201: - Built-in type `instruction` renamed to `guideline`; the core type registry consolidated on `guideline`/`memory`/`note`. The migration target slug, the TERM_GUIDELINE constant, and the content-guidelines singleton follow. - Type titles now use the `_x( ..., 'knowledge type' )` context. - Capability filter `_wp_knowledge_synthesize_caps` becomes the public `wp_maybe_grant_knowledge_caps`, hooked at priority 1 to match the `wp_maybe_grant_*` family. - Disable autosave support for `wp_knowledge` (headless storage with no editor session); revision history is retained. - Register the `wp_knowledge_type` taxonomy as headless (`show_ui => false`). - Align the private-status REST error message with core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Aligns the experimental Knowledge implementation with the refined version ported to WordPress core in WordPress/wordpress-develop#12201: - Built-in type `instruction` renamed to `guideline`; the core type registry consolidated on `guideline`/`memory`/`note`. The migration target slug, the TERM_GUIDELINE constant, and the content-guidelines singleton follow. - Type titles now use the `_x( ..., 'knowledge type' )` context. - Capability filter `_wp_knowledge_synthesize_caps` becomes the public `wp_maybe_grant_knowledge_caps`, hooked at priority 1 to match the `wp_maybe_grant_*` family. - Disable autosave support for `wp_knowledge` (headless storage with no editor session); revision history is retained. - Register the `wp_knowledge_type` taxonomy as headless (`show_ui => false`). - Align the private-status REST error message with core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| * @param WP_User $user The user object. | ||
| * @return bool[] Filtered array of the user's capabilities. | ||
| */ | ||
| function wp_maybe_grant_knowledge_caps( array $allcaps, array $caps, array $args, WP_User $user ): array { |
There was a problem hiding this comment.
Core has historically avoided scalar/array hints on filter callbacks for graceful coercion see wp_maybe_grant_site_health_caps above. But it is new function although not following the previous patterns maybe we should embrace it.
There was a problem hiding this comment.
Right, that might make sense.
Aside, the function might need to be marked as private and prefixed with _.
There was a problem hiding this comment.
A coding standards change was made some time ago to avoid the _ prefix for private functions so it can just be annotated as such without the underscore.
|
Knowledge, is something done mostly though for LLMS, similar to abilities, I wonder if for knowledge we avoid a specific REST endpoint and we just use abilities as the interface to manage the knowledge everywhere? Instead of calling REST we would always call the abilities. |
| ! $post instanceof WP_Post || | ||
| 'wp_knowledge' !== $post->post_type || | ||
| (int) $post->post_author !== (int) $user->ID || | ||
| 'private' !== $post->post_status |
There was a problem hiding this comment.
I think contributor-level users can likely trash their own private knowledge, then lose the ability to permanently delete it. Because grant only applies when post_status === 'private', so once the item is in trash, delete_knowledge is no longer granted.
| ), | ||
| 'capabilities' => array( | ||
| 'manage_terms' => 'manage_options', | ||
| 'edit_terms' => 'edit_knowledge', |
There was a problem hiding this comment.
I think we may have an issue here, it seems contributors will be able to create new knowledge_types e.g: over REST, because edit_terms maps to edit_knowledge and contributors will get edit_knowledge.
There was a problem hiding this comment.
Yes, this is intentional because we lazily insert new taxonomy terms due to performance issues we had with inserting an upfront predefined list of terms. Therefore, when adding a post, the user needs have a way to create a term, too.
There was a problem hiding this comment.
naming things for caps for plural/singular: knowledge_types, knowledge_type or similar.
| $request = new WP_REST_Request( 'GET', '/wp/v2/knowledge/' . self::$admin_private ); | ||
| $response = rest_get_server()->dispatch( $request ); | ||
|
|
||
| $this->assertContains( $response->get_status(), array( 401, 403 ) ); |
There was a problem hiding this comment.
Nit: here we testing something not authorized I think testing 403 should be enough.
| '_builtin' => true, | ||
| 'show_in_nav_menus' => false, | ||
| 'show_in_rest' => true, | ||
| 'show_admin_column' => true, |
There was a problem hiding this comment.
Given that show_ui for the post type and taxonomy are both false, show_admin_column true does nothing.
There was a problem hiding this comment.
Here, we should decide whether we want to allow the UI for managing taxonomy terms for wp_knowledge_type. I think it's something that is currently enabled in trunk on Gutenberg.
There was a problem hiding this comment.
I think that UI appears as a submenu of the post type menu item, as the post type does not have a menu item, the flag does nothing on this case.
| ); | ||
|
|
||
| register_post_type( | ||
| 'wp_knowledge', |
There was a problem hiding this comment.
We are not setting delete_with_user, so we use the default and knowledge created by an user is deleted when that user is deleted. I guess that behaviour is correct if the knowledge is private, but it may have unintended consequences for knowledge memory shared between users.
| $post_type = get_post_type_object( $this->post_type ); | ||
| if ( ! current_user_can( $post_type->cap->read ) ) { | ||
| return new WP_Error( | ||
| 'rest_forbidden', |
There was a problem hiding this comment.
src/wp-includes/rest-api/endpoints/class-wp-rest-font-families-controller.php for a similar check uses error rest_cannot_read. I think both are ok, but maybe we should align?
| * @group knowledge | ||
| * @group post | ||
| */ | ||
| class Tests_Knowledge_PostType extends WP_UnitTestCase { |
There was a problem hiding this comment.
Maybe we should add a test here where Contributor edit/delete their own knowlodge via REST (200) and and then see it 403 on others' knowlodge.
|
Great work here, things look, just left some comments for considering but it seems to be on a good shape. |
peterwilsoncc
left a comment
There was a problem hiding this comment.
I've added a few first pass notes inline...
| * | ||
| * @param int $post_id Saved post ID. | ||
| */ | ||
| function _wp_knowledge_ensure_default_type_term( int $post_id ): void { |
There was a problem hiding this comment.
Per earlier comment, requires changes elsewhere.
| function _wp_knowledge_ensure_default_type_term( int $post_id ): void { | |
| function wp_knowledge_ensure_default_type_term( int $post_id ): void { |
| * @phpstan-param array<non-empty-string, mixed> $data | ||
| * @phpstan-return array<non-empty-string, mixed> | ||
| */ | ||
| function _wp_knowledge_maybe_map_term_label( array $data, string $taxonomy ): array { |
There was a problem hiding this comment.
Per earlier comment, requires changes elsewhere.
| function _wp_knowledge_maybe_map_term_label( array $data, string $taxonomy ): array { | |
| function wp_knowledge_maybe_map_term_label( array $data, string $taxonomy ): array { |
| /* | ||
| * "Knowledge" is a mass noun, so the singular and plural capability | ||
| * bases must differ: with both set to `knowledge`, the generated | ||
| * per-post meta caps (`edit_knowledge_item`) would collide with the | ||
| * primitive caps (`edit_knowledge`). The `*_knowledge_item` forms are | ||
| * never granted directly; `map_meta_cap()` resolves them onto the | ||
| * primitives, which `wp_maybe_grant_knowledge_caps()` synthesizes. | ||
| */ | ||
| 'capability_type' => array( 'knowledge_item', 'knowledge' ), |
There was a problem hiding this comment.
| /* | |
| * "Knowledge" is a mass noun, so the singular and plural capability | |
| * bases must differ: with both set to `knowledge`, the generated | |
| * per-post meta caps (`edit_knowledge_item`) would collide with the | |
| * primitive caps (`edit_knowledge`). The `*_knowledge_item` forms are | |
| * never granted directly; `map_meta_cap()` resolves them onto the | |
| * primitives, which `wp_maybe_grant_knowledge_caps()` synthesizes. | |
| */ | |
| 'capability_type' => array( 'knowledge_item', 'knowledge' ), | |
| 'capability_type' => array( 'knowledge_items', 'knowledge_item' ), |
...or similar: as a general rule primitives are plural and meta are the singular form of the same. Eg edit_posts allows editing of posts, whereas edit_post allows the editing of a single post.
Requires changes elsewhere.
| ), | ||
| 'capabilities' => array( | ||
| 'manage_terms' => 'manage_options', | ||
| 'edit_terms' => 'edit_knowledge', |
There was a problem hiding this comment.
naming things for caps for plural/singular: knowledge_types, knowledge_type or similar.
Trac ticket: https://core.trac.wordpress.org/ticket/65476
Summary
Introduces
wp_knowledge, a private-by-default custom post type that acts as a storage primitive for structured site knowledge (guidelines, memories, notes), along with itswp_knowledge_typetaxonomy and a dedicated REST controller.This is the WordPress core counterpart to the Knowledge storage primitive being explored in Gutenberg (WordPress/gutenberg#79149). It deliberately lands the storage primitive only — the Guidelines admin UI and the
content-guidelinessingleton remain consumer-side concerns and are out of scope here.Details
wp_knowledgeandwp_knowledge_typeare registered as built-ins increate_initial_post_types()/create_initial_taxonomies(). Both are non-public and headless (show_ui => false); rows are managed via the REST API rather than a wp-admin screen.WP_REST_Knowledge_Controllerserves/wp/v2/knowledge. Reads require an authenticated user with the read capability, collection queries are scoped to rows the current user can read (so totals/pagination respect per-user visibility), callers without the publish capability are limited to theprivatestatus, and new rows default toprivate. Revision history uses the default revisions controller; autosave endpoints are disabled (following thewp_global_stylesprecedent), since knowledge has no editor session.user_has_capfilter (wp_maybe_grant_knowledge_caps()), matching the existingwp_maybe_grant_*pattern. Administrators manage all knowledge; contributors and above may create and fully manage their own private rows. Subscribers and anonymous users are blocked at the post-type door.wp_knowledge_types()exposes a filterable set of types (guideline,memory,note); rows saved without a type fall back to thenoteterm.Testing
New unit tests cover registration, the capability matrix across all roles, the REST controller (CRUD, permission gating, private-by-default), and the type registry. The REST route snapshot in
tests/phpunit/tests/rest-api/rest-schema-setup.phpis updated accordingly.Note
The large
tests/qunit/fixtures/wp-api-generated.jsdiff is the auto-generated REST API client fixture, regenerated to add the new/wp/v2/knowledgeroutes and thewp_knowledge/wp_knowledge_typeentries.🤖 Generated with Claude Code